/*
 * Copyright 2006-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package asphalt2.container
{
	import asphalt2.event.ModulePageContainerEvent;
	import asphalt2.event.PageContainerEvent;
	import asphalt2.util.UIUtil;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.system.ApplicationDomain;
	import flash.utils.getDefinitionByName;
	
	import mx.containers.ViewStack;
	import mx.core.Container;
	import mx.core.ContainerCreationPolicy;
	import mx.core.UIComponent;
	import mx.effects.Effect;
	import mx.effects.Fade;
	import mx.events.EffectEvent;
	import mx.events.FlexEvent;
	import mx.events.ModuleEvent;
	import mx.modules.IModuleInfo;
	import mx.modules.Module;
	import mx.modules.ModuleManager;
	

	[Effect(name="pageOutEffect")]
	[Effect(name="pageInEffect")]
	
	/**
	 * ページ表示用コンテナクラス
	 * PageContainerEventをディスパッチすると、このコンテナクラスが受取り、
	 *　画面を切り替えます。
	 * @author c9katayama
	 **/	
	public class PageContainer extends ViewStack
	{
		/** ページが消えるときにエフェクト */
		private var _pageOutEffect:Effect;
		/** ページが表示されるときにエフェクト */
		private var _pageInEffect:Effect;
		/** historyManager利用時、スタックしておくViewの数*/
		private var _historyStackNum:int = 1;
		/**処理中にビジーアイコンを出すかどうか*/
		protected var _showBusyCursor:Boolean = true;
		
		//内部処理用
		/**イベントをリッスンする対象ルートオブジェクト*/
		protected var listenRoot:DisplayObject;		
		/** 処理中のPageContainerEvent */
		protected var processingEventObject:PageContainerEvent;
		/**　処理中かどうかのフラグ　*/
		protected var procesing:Boolean = false;
		/** ロード中のModuleInfo */
		protected var processingModuleInfo:IModuleInfo;
				
		public function set pageOutEffect(effect:Effect):void{
			_pageOutEffect = effect;	
		}
		public function get pageOutEffect():Effect{			
			return _pageOutEffect;
		}
		public function set pageInEffect(effect:Effect):void{
			_pageInEffect = effect;
			setStyle("creationCompleteEffect",pageInEffect);
		}		
		public function get pageInEffect():Effect{
			return _pageInEffect;
		}
		public function set historyStackNum(num:int):void{			
			this._historyStackNum = num;
			if(num>1){
				historyManagementEnabled=true;
			}
		}
		public function get historyStackNum():int{
			return _historyStackNum;
		}
		public function set showBusyCursor(value:Boolean):void{
			this._showBusyCursor = value;
		}
		public function get showBusyCursor():Boolean{
			return _showBusyCursor;
		}

		public function PageContainer(){
			super();
			creationPolicy=ContainerCreationPolicy.ALL;
			UIUtil.addOnceEventListener(this,FlexEvent.CREATION_COMPLETE,
				function(e:FlexEvent):void{					
					setup();
				});
			setDefaultEffect();
		}
		protected function setDefaultEffect():void{
			//デフォルトのエフェクト
			var defaultPageOut:Fade = new Fade();
			defaultPageOut.alphaFrom = 1;
			defaultPageOut.alphaTo = 0;
			defaultPageOut.duration=300;
			this.pageOutEffect = defaultPageOut;
			var defaultPageIn:Fade = new Fade();
			defaultPageIn.alphaFrom = 0;
			defaultPageIn.alphaTo = 1;
			defaultPageIn.duration=300;
			this.pageInEffect = defaultPageIn;
		}
		protected function setup():void{
			this.listenRoot = UIUtil.findRoot();
			listenRoot.addEventListener(PageContainerEvent.NEXTPAGE,handleNextPage);
			listenRoot.addEventListener(Event.REMOVED,handlePageContainerRemove);
			if(pageInEffect != null){
				pageInEffect.addEventListener(EffectEvent.EFFECT_END,handlePageInEffectEnd);
			}
			//エフェクト追加
			var children:Array = getChildren();
			for(var i:int = 0;i < children.length;i++){
				applyPageEffect(UIComponent(children[i]));
			}
		}
		protected function handleNextPage(e:PageContainerEvent):void{
			if(this.id == e.targetId){
				if(!procesing){
					processStart(e);
					if(e is ModulePageContainerEvent){
						loadNextModulePage(ModulePageContainerEvent(e));
					}else{
						loadNextPage(e);
					}
				}
			}
		}
		protected function loadNextModulePage(modulePageEvent:ModulePageContainerEvent):void{
			processingModuleInfo = ModuleManager.getModule(modulePageEvent.modulePath);
			processingModuleInfo.addEventListener(ModuleEvent.ERROR,function(e:ModuleEvent):void{
				trace("Module load ERROR.Module="+processingModuleInfo.url);
				processEnd();
				throw e;
			});
			processingModuleInfo.addEventListener(ModuleEvent.READY,function(e:ModuleEvent):void{
				//nextPageClassNameがある場合も、CSSロードのために一旦インスタンス化
				var nextPage:Container = Module(e.module.factory.create());
				if(modulePageEvent.nextPageClassName != null){
					var nextPageClass:Class = getDefinitionByName(modulePageEvent.nextPageClassName) as Class;
					nextPage = Container(new nextPageClass());
				}
				//CSSのセレクタがかぶっていたりするとインスタンス化に失敗する
				if(nextPage == null){
					throw new Error("Cannot instanceate Module. module="+processingModuleInfo.url+". Check MXML,AS,CSS.");
				}
				applyParameters(nextPage,modulePageEvent.parameters);
				applyPageEffect(nextPage);
				handlePageReady(nextPage);
			});
			processingModuleInfo.load(ApplicationDomain.currentDomain);			
		}
		protected function loadNextPage(e:PageContainerEvent):void{
			var nextPage:Container = Container(new processingEventObject.nextPage());
			applyParameters(nextPage,processingEventObject.parameters);
			applyPageEffect(nextPage);
			handlePageReady(nextPage);			
		}		
		protected function applyPageEffect(child:UIComponent):void{
			var comp:UIComponent = child as UIComponent;
			var inEffect:Effect = comp.getStyle("showEffect") as Effect;
			if(inEffect == null){
				comp.setStyle("showEffect",pageInEffect);
			}else{
				inEffect.addEventListener(EffectEvent.EFFECT_END,handlePageInEffectEnd);
			}
			var outEffect:Effect = comp.getStyle("hideEffect") as Effect; 
			if(outEffect == null){
				comp.setStyle("hideEffect",pageOutEffect);
			}
		}		
		protected function applyParameters(nextPage:Container,parameters:Object):void{
			if(parameters != null){
				try{
					if(nextPage.hasOwnProperty("parameters")){
						nextPage["parameters"] = parameters;
					}
					for(var key:String in parameters){
						if(nextPage.hasOwnProperty(key)){
							nextPage[key] = parameters[key];
						}
					}
				}catch(e:Error){
					processEnd();
					throw e;
				}
			}				
		}

		protected function processStart(e:PageContainerEvent):void{
			procesing=true;
			if(showBusyCursor){
				cursorManager.setBusyCursor();
			}
			processingEventObject = e;
		}
		protected function processEnd():void{
			procesing=false;
			if(showBusyCursor){
				cursorManager.removeBusyCursor();
			}
			processingEventObject=null;
			processingModuleInfo = null;
		}
		protected function handlePageReady(nextPage:Container):void{			
			UIUtil.addOnceEventListener(nextPage,FlexEvent.CREATION_COMPLETE,
				function(e:FlexEvent):void{										
					if(selectedChild!=nextPage){
						//エフェクトないときはshowで消す
						if(pageInEffect==null){
							UIUtil.addOnceEventListener(nextPage,FlexEvent.SHOW,function(e:FlexEvent):void{
								handlePageInEffectEnd(null);
							});
						}
						selectedChild = nextPage;//表示を自分に切り替え
					}else{
						//PageContainerに初期セットされたコンポーネントがなく、
						//かつはじめのコンポーネントが追加された時はselectedChild=nextPage
						if(pageInEffect == null){
							//この場合はShowはも呼ばれないので、処理終了
							handlePageInEffectEnd(null);
						}else{
							pageInEffect.play([nextPage]);
						}
					}
				}
			);
			addChild(nextPage);		
		}
		protected function handlePageInEffectEnd(e:EffectEvent):void{
			//一定数以上は破棄
			var removeNum:int = getChildren().length-historyStackNum;
			for(var i:int = 0;i < removeNum;i++){
				removeChildAt(0);
			}
			processEnd();
		}
		protected function handlePageContainerRemove(e:Event):void{
			if(UIUtil.isParentOrThis(this,DisplayObject(e.target))){
				trace("dispose"+this);
				this.removeAllChildren();
				listenRoot.removeEventListener(PageContainerEvent.NEXTPAGE,handleNextPage);
				listenRoot.removeEventListener(Event.REMOVED,handlePageContainerRemove);
				listenRoot = null;
				_pageOutEffect = null;
				_pageInEffect = null;
				processEnd();
			}			
		}
		
	    //  Methods: IHistoryManagerClient
	    /**
	     * HistoryManagerをonの状態でViewStackからremoveすると、URLにViewStackに入っているよりも
	     * 大きなインデックスが記録されてしまう為、saveStateから最大履歴数以上のインデックスを返さないように
	     * する処理
	     */
	    public override function saveState():Object
	    {
	    	var maxIndex:int = historyStackNum-1;
	        var index:int = (selectedIndex>=maxIndex) ? maxIndex : selectedIndex;	        
	        return { selectedIndex: index };
	    }
	}
}